
// Copyright (C) 2007 Richard Uhler
// This file is part of Esiply.
//
// Esiply is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <assert.h>

#include "Boolean.h"
#include "ErrorObject.h"
#include "Function.h"
#include "InternalFunction.h"
#include "Object.h"
#include "Null.h"
#include "String.h"
#include "Undefined.h"


Value* fproto_func(Value* this_obj, List* args)
{
    // Ecma 15.3.4
    // The Function prototype object is itself a Function object that, when
    // invoked, accepts any arguments and returns undefined.
    return Undefined::undefined;
}

Function* Function::prototype = new InternalFunction(fproto_func, NULL, 1);

Function::Function()
{
	this->m_sync = 0;
    // Every function has a prototype property
    this->put(new String("prototype"), new Object(), PA_DONT_DELETE);
}

bool Function::isCallable() const
{
    return true;
}

Value* Function::hasInstance(Value* value)
{
    // Ecma 15.3.5.3 [[HasInstance]]
    // 1. If value is not an object, return false
    if (value->getType() != VT_OBJECT)
    {
        return Boolean::False;
    }

    // 2. Call the [[get]] method of F with property name prototoype
    // 3. Let obj be that result
    Value* obj = get(new String("prototype"));

    // 4. if obj is not an object, throw a TypeError exception
    if (obj->getType() != VT_OBJECT)
    {
        return esiply_throw(ET_TYPE, "prototype is not an object");
    }

    Value* v = value;
    while (true)
    {
        // 5. Let v be the value of the [[prototoype]] property of v
        v = v->object()->get_prototype();
    
        // 6. if V is null, return false
        if (v == NULL)
        {
            return Boolean::False;
        }

        // 7. If obj and v refer to the same object, return true
        if (obj == v)
        {
            return Boolean::True;
        }

        // 8. go to step 5.
    }

    assert(false);
}
int Function::get_sync() const
{
    return m_sync;
}

void Function::set_sync(int sync)
{
    m_sync = sync;
}
